package pt.uma.gap.graph;


import java.util.ArrayList;
import java.util.List;

import pt.uma.gap.accessperm.Utilities;
import pt.uma.gap.ast.MethodASTInfo;

/**
 * @author Duarte Abreu
 *
 */
public class MethodNode {

	private String name;
	private String packageName;
	private String className;
	private String returnType;
	//private List<GraphNode> graphNodeList = new ArrayList<GraphNode>();

	private List<GraphNode> graphNodeRefList = new ArrayList<GraphNode>();
	private List<GraphNode> graphNodeMemList = new ArrayList<GraphNode>();

	private List<WriteEdge> writeEdgeList = new ArrayList<WriteEdge>();
	private List<ReadEdge> readEdgeList = new ArrayList<ReadEdge>();

	private List<PointeeEdge> pointeeEdgeList = new ArrayList<PointeeEdge>();

	private String context;


	private GraphNode fooNode;
	private GraphNode contextNode;
	private String completeName;

	private boolean accessPermLoaded = false;
	private boolean rulesApplied = false;

	private MethodASTInfo methodASTInfo;
	
	private List<MethodNode> callingMethodList = new ArrayList<MethodNode>();

	public MethodNode() {

		fooNode = new GraphNode("Foo", GraphNode.NodeType.FOO, null);
		contextNode = new GraphNode("Context", GraphNode.NodeType.CONTEXT, null);
	}

	public MethodNode(String name, String returnType) {
		this();
		this.name = name;
		this.returnType = returnType;
	}

	public String getName() {
		return name;
	}

	public String getCompleteName(){
		if(completeName == null){
			for (GraphNode graphNode : graphNodeRefList) {
				if(graphNode.getOrigin().equals(GraphNode.NoteOrigin.PARAM)){
					if(completeName != null){
						completeName += "," + graphNode.getValueType();
					}else{
						completeName = graphNode.getValueType();
					}
				}
			}
			completeName = getName() + "(" + completeName + ")";
		}
		return  completeName;
	}

	public String getReturnType() {
		return returnType;
	}

	public void setName(String methodName) {
		this.name = methodName;

	}

	public void setReturnType(String returnType) {
		this.returnType = returnType;

	}



	public void setWriteEdgeList(List<WriteEdge> writeEdgeList) {
		this.writeEdgeList = writeEdgeList;
	}

	public void addWriteEdgeList(WriteEdge writeEdge){
		writeEdgeList.add(writeEdge);
	}

	public List<ReadEdge> getReadEdgeList() {
		return readEdgeList;
	}

	public void setReadEdgeList(List<ReadEdge> readEdgeList) {
		this.readEdgeList = readEdgeList;
	}

	public void addReadEdgeList(ReadEdge readEdge){
		readEdgeList.add(readEdge);
	}

	public List<WriteEdge> getWriteEdgeList() {
		return writeEdgeList;
	}



	public void add(GraphNode graphNode) {
		if(graphNode.getType().equals(GraphNode.NodeType.MEM)){
			graphNodeMemList.add(graphNode);
		}else if(graphNode.getType().equals(GraphNode.NodeType.REF)){
			graphNodeRefList.add(graphNode);
		}

	}


	public GraphNode getFooNode() {
		return fooNode;
	}

	public void setFooNode(GraphNode fooNode) {
		this.fooNode = fooNode;
	}

	public GraphNode getContextNode() {
		return contextNode;
	}

	public void setContextNode(GraphNode contextNode) {
		this.contextNode = contextNode;
	}

	public void addEdge(ReadEdge readEdge) {


		addReadEdgeList(readEdge);


	}

	public void addEdge(WriteEdge writeEdge) {
		addWriteEdgeList(writeEdge);
	}




	public List<PointeeEdge> getPointeeEdgeList() {
		return pointeeEdgeList;
	}

	public void addEdge(PointeeEdge pointeeEdge) {

		pointeeEdgeList.add(pointeeEdge);
	}

	/**
	 * Find the memory node from a reference node
	 * @param graphNodeRef Reference graph node
	 * @return The memory object pointed by reference node
	 */
	public GraphNode getGraphNodeMem(GraphNode graphNodeRef){
		for (PointeeEdge pointeeEdge : pointeeEdgeList) {
			if(pointeeEdge.getFrom().equals(graphNodeRef)){
				return pointeeEdge.getTo();
			}
		}
		return null;
	}

	/**
	 * Find a reference node by is name
	 * @param expression
	 * @return Graph node with name equals to expression text
	 */
	public GraphNode getGraphNodeRefByExpression(String expression)  {

		String varName = expression;

		if(varName.startsWith("this.")){
			varName = varName.replace("this.", Utilities.FIELD_TAG);

			//1st Check on class Fields
			for (GraphNode graphNode : graphNodeRefList) {
				if(graphNode.getOrigin().equals(GraphNode.NoteOrigin.FIELD)){
					if(varName.equals(graphNode.getName())){
						return graphNode;
					}
				}
			}
		}else{
			//2nd Check on local variables and parameters
			for (GraphNode graphNode : graphNodeRefList) {
				if(!graphNode.getOrigin().equals(GraphNode.NoteOrigin.FIELD)){
					if(expression.equals(graphNode.getName())){
						return graphNode;
					}
				}
			}
		}

		if(!varName.startsWith("this.")){
			varName = Utilities.FIELD_TAG + varName;
			//3st Check on class Fields
			for (GraphNode graphNode : graphNodeRefList) {
				if(graphNode.getOrigin().equals(GraphNode.NoteOrigin.FIELD)){
					if(varName.equals(graphNode.getName())){
						return graphNode;
					}
				}
			}
		}
		return null;
	}

	public void setContext(String context) {
		this.context = context;
	}

	public String getContext() {
		return context;
	}

	public List<GraphNode> getGraphNodeMemList() {
		return graphNodeMemList;
	}

	public List<GraphNode> getGraphNodeRefList() {

		return graphNodeRefList;
	}

	public void setAccessPermLoaded(boolean accessPermLoaded) {
		this.accessPermLoaded = accessPermLoaded;
	}

	public boolean isAccessPermLoaded() {
		return accessPermLoaded;
	}

	public void setMethodASTInfo(MethodASTInfo methodASTInfo) {
		this.methodASTInfo = methodASTInfo;
	}

	public MethodASTInfo getMethodASTInfo() {
		return methodASTInfo;
	}

	public void addCallingMethodList(MethodNode methodNode) {
		this.callingMethodList.add(methodNode);
	}

	public List<MethodNode> getCallingMethodList() {
		return callingMethodList;
	}

	public void setRulesApplied(boolean rulesApplied) {
		this.rulesApplied = rulesApplied;
	}

	public boolean isRulesApplied() {
		return rulesApplied;
	}

	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}

	public String getPackageName() {
		return packageName;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String getClassName() {
		return className;
	}


	/**
	 * @return Path with package name + class name + method name with parameters
	 */
	public String getFullPath(){
		return packageName + "." + className + "."+ getCompleteName();
	}

	public GraphNode getParamGraphNodeRefByPos(int i) {
		for (GraphNode graphNode : graphNodeRefList) {
			if(graphNode.getOrigin().equals(GraphNode.NoteOrigin.PARAM) && (graphNode.getPosition() == i)){
				return graphNode;
			}
		}
		return null;
	}

}
